package com.db.concurrency;

import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

public class ExpiringMemoizer<A, V> implements Computable<A, V> {
  private final Computable<A, V> computer;
  private final Cache<A, V> cache;

  public ExpiringMemoizer(Computable<A, V> computer, long duration,
      TimeUnit unit) {
    this.computer = computer;
    this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
        .build();
  }

  @Override
  public V compute(final A arg) throws InterruptedException {
    while (true) {
      try {
        return cache.get(arg, new Callable<V>() {
          public V call() throws InterruptedException {
            return computer.compute(arg);
          }
        });
      } catch (CancellationException e) {
        cache.invalidate(arg);
      } catch (ExecutionException e) {
        cache.invalidate(arg);
        throw new RuntimeException(e);
      }
    }
  }
}

class VeryExpensiveComputation implements Computable<String, BigInteger> {

  @Override
  public BigInteger compute(String arg) throws InterruptedException {
    return new BigInteger(arg);
  }

}